part of '../widgets.dart';

typedef VanFormLabelAlign = VanFieldLabelAlign;
typedef VanFormInputAlign = VanFieldTextAlign;
typedef VanFormErrorMessageAlign = VanFieldTextAlign;
typedef VanFormValidateTrigger = VanValidateTrigger;

typedef VanFormValue = Map<String, dynamic>;

class VanFormErrorInfo {
  static const empty = VanFormErrorInfo();
  final VanFormValue values;
  final List<VanFieldValidateError> errors;

  const VanFormErrorInfo([this.values = const {}, this.errors = const []]);

  @override
  String toString() {
    return "{ values: $values, errors: $errors }";
  }
}

typedef VanFormOnSubmit = Function(Map<String, dynamic> values);
typedef VanFormOnFailed = Function(VanFormErrorInfo errorInfo);

class VanFieldValidation<T> {
  final String name;
  final List? rules;
  final VanGetter<T>? value;
  final VoidCallback clear;
  final VanSetter<VanFieldValidateScope> scope;

  const VanFieldValidation(this.name, {this.rules, this.value, required this.scope, required this.clear});
}

class VanFormController extends VanCtrl<VanFormErrorInfo> {
  final bool single;

  VanFormController({this.single = true}) : super(VanFormErrorInfo.empty);

  final List<VanFormState> forms = [];

  /// 提交表单，与点击提交按钮的效果等价
  submit() async {
    List<Future> futures = [];
    for (var form in forms) {
      futures[futures.length] = form.submit();
    }
    await Future.wait(futures);
  }

  /// 获取所有表单项当前的值(默认: Record<string, unknown>)
  VanFormValue getValues() {
    return {};
  }

  /// 验证表单，支持传入一个或多个 name 来验证单个或部分表单项，不传入 name 时，会验证所有表单项(默认: Future<void>)
  Future<void> validate({String? name, List<String>? names}) {
    return Future.value();
  }

  /// 重置表单项的验证提示，支持传入一个或多个 name 来重置单个或部分表单项，不传入 name 时，会重置所有表单项
  void resetValidation({String? name, List<String>? names}) {
    final errorInfo = value;
    if (errorInfo.errors.isEmpty) return;
    if (name == null && names == null) {
      value = VanFormErrorInfo(errorInfo.values, []);
      return;
    }
    final errors = errorInfo.errors;
    if (name != null) errors.remove(name);
    if (names?.isNotEmpty == true) {
      for (var name1 in names!) {
        errors.remove(name1);
      }
    }
    value = VanFormErrorInfo(errorInfo.values, errors);
  }

  /// 获取所有表单项的校验状态，状态包括 passed、failed、unvalidated(默认: Record<string, [VanFieldValidationStatus]>)
  Map<String, VanFieldValidationStatus> getValidationStatus() {
    return {};
  }

  /// 滚动到对应表单项的位置，默认滚动到顶部，第二个参数传 false 可滚动至底部
  void scrollToField({required String name, required bool alignToTop}) {}
}

/// 用于数据录入、校验，支持输入框、单选框、复选框、文件上传等类型，需要与 [VanField] 输入框 组件搭配使用。
class VanForm extends StatefulWidget {
  /// props - 表单项 label 宽度，默认单位为px(默认: 6.2 * 14)
  final double? labelWidth;

  /// props - 表单项 label 对齐方式，可选值为 center right top(默认: left)
  final VanFormLabelAlign labelAlign;

  /// props - 输入框对齐方式，可选值为 center right(默认: left)
  final VanFormInputAlign inputAlign;

  /// props - 错误提示文案对齐方式，可选值为 center right(默认: left)
  final VanFormErrorMessageAlign errorMessageAlign;

  /// props - 表单校验触发时机，可选值为 onChange、onSubmit，支持通过数组同时设置多个值，具体用法见下方表格(默认: onBlur)
  final VanFormValidateTrigger validateTrigger;

  /// props - 表单校验规则，详见 Form 组件
  /// <br> **list - type** support **[Map]** or **[VanFieldRule]**
  final Map<String, List> rules;

  /// props - 是否在 label 后面添加冒号(默认: false)
  final bool colon;

  /// props - 是否禁用表单中的所有输入框(默认: false)
  final bool disabled;

  /// props - 是否将表单中的所有输入框设置为只读状态(默认: false)
  final bool readonly;

  /// props - 是否显示表单必填星号, 不设定代表"auto"
  final bool? required;

  /// props - 是否在某一项校验不通过时停止校验(默认: false)
  final bool validateFirst;

  /// props - 是否在提交表单且校验不通过时滚动至错误的表单项(默认: false)
  final bool scrollToError;

  /// props - 是否在校验不通过时标红输入框(默认: false)
  final bool showError;

  /// props - 是否在校验不通过时在输入框下方展示错误提示(默认: true)
  final bool showErrorMessage;

  /// props - 是否在按下回车键时提交表单(默认: true)
  final bool submitOnEnter;

  /// 提交表单且验证通过后触发
  final VanFormOnSubmit? onSubmit;

  ///提交表单且验证不通过后触发
  final VanFormOnFailed? onFailed;

  /// slots - 默认插槽
  final Widget? child;

  /// slots - 默认插槽
  final List<Widget>? children;

  static VanFormState? of(BuildContext context) {
    return VanProvider.of<VanFormState>(context);
  }

  const VanForm({
    super.key,
    this.labelWidth,
    this.labelAlign = VanFormLabelAlign.left,
    this.inputAlign = VanFormInputAlign.left,
    this.errorMessageAlign = VanFormErrorMessageAlign.left,
    this.validateTrigger = VanFormValidateTrigger.onBlur,
    this.rules = const {},
    this.colon = false,
    this.disabled = false,
    this.readonly = false,
    this.required,
    this.validateFirst = false,
    this.scrollToError = false,
    this.showError = false,
    this.showErrorMessage = true,
    this.submitOnEnter = true,
    this.onSubmit,
    this.onFailed,
    this.child,
    this.children,
  });

  @override
  State<VanForm> createState() => VanFormState();
}

typedef _VanFormStatus = Map<String, VanFieldValidationStatus>;

class VanFormState extends State<VanForm> with VanProvide {
  final List<VanFieldController> _ctrls = [];
  final List<VanFieldValidation> _validations = [];
  final Ref<VanFormErrorInfo> _errorInfoRef = ref(const VanFormErrorInfo());
  final Ref<_VanFormStatus> _status = ref<_VanFormStatus>(const {});
  late VanFormController _controller;

  Future submit() async {
    if (_validations.isEmpty) return;
    List<Future> futures = [];
    final VanFormValue values = {};
    final List<VanFieldValidateError> errors = [];
    for (var validation in _validations) {
      if (widget.validateFirst && errors.isNotEmpty) {
        break;
      }
      final name = validation.name;
      final List rules = [];

      if (validation.rules is List) rules.addAll(validation.rules!);
      if (widget.rules[name] != null) rules.addAll(widget.rules[name]!);
      if (rules.isEmpty) continue;

      dynamic value;
      if (validation.value != null) {
        value = validation.value!();
      } else {
        value = null;
      }
      values[name] = value;
      final Future future = Future(() async {
        String? message;
        VanFieldValidationStatus status = VanFieldValidationStatus.verifying;
        validation.scope(VanFieldValidateScope(status));
        for (var rule in rules) {
          message = await VanFieldRule.fromValid(rule, value).catchError((error) {
            String msg = "rule.valid.error: [$name]";
            if (kDebugMode) _logger.e(msg, error: error);
            return msg;
          });

          if (message?.isNotEmpty == true) {
            break;
          }
        }
        if (message?.isNotEmpty == true) {
          status = VanFieldValidationStatus.failed;
          errors.add(VanFieldValidateError(name, message!));
          validation.scope(VanFieldValidateScope(status, message));
        } else {
          status = VanFieldValidationStatus.passed;
          validation.scope(VanFieldValidateScope(status));
        }

        return "$name:${message ?? ''}";
      });
      futures.add(future);
    }
    await Future.wait(futures);
    final errorInfo = VanFormErrorInfo(values, errors);
    _errorInfoRef.value = errorInfo;
    if (errors.isEmpty) {
      if (widget.onSubmit != null) widget.onSubmit!(values);
    } else {
      if (widget.onFailed != null) widget.onFailed!(errorInfo);
    }
    return errorInfo;
  }

  reset() {}

  void removeFieldValidation(VanFieldValidation validation) {
    _validations.remove(validation);
  }

  VanFieldValidation<T> addFieldValidation<T>({
    required String name,
    dynamic rules,
    VanGetter<T>? value,
    required VanSetter<VanFieldValidateScope> scope,
    required VoidCallback clear,
  }) {
    final validation = VanFieldValidation(
      name,
      rules: rules,
      value: value,
      scope: scope,
      clear: clear,
    );
    _validations.add(validation);
    return validation;
  }

  @override
  Widget build(BuildContext context) {
    List<Widget> children0 = [];
    if (widget.child != null) children0.add(widget.child!);
    if (widget.children?.isNotEmpty != null) children0.addAll(widget.children!);
    if (children0.isEmpty) return Container();
    if (children0.length == 1) return children0.first;
    return VanProvider(provide: this, child: Column(children: children0));
  }
}
